package cn.iocoder.yudao.module.lfd.controller.admin.appointment;

import cn.hutool.core.lang.Pair;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.lfd.controller.admin.appointment.vo.*;
import cn.iocoder.yudao.module.lfd.convert.appointment.AppointmentConvert;
import cn.iocoder.yudao.module.lfd.dal.dataobject.appointment.AppointmentDO;
import cn.iocoder.yudao.module.lfd.dal.dataobject.appointment.AppointmentListDO;
import cn.iocoder.yudao.module.lfd.dal.dataobject.appointment.AppointmentStatictis;
import cn.iocoder.yudao.module.lfd.dal.dataobject.barber.BarberDO;
import cn.iocoder.yudao.module.lfd.dal.dataobject.shop.LfdShopDO;
import cn.iocoder.yudao.module.lfd.pay.PayOrderService;
import cn.iocoder.yudao.module.lfd.service.appointment.AppointmentService;
import cn.iocoder.yudao.module.lfd.service.appointmentitem.AppointmentItemService;
import cn.iocoder.yudao.module.lfd.service.barber.BarberService;
import cn.iocoder.yudao.module.lfd.service.shop.ShopService;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.StreamUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;

@Tag(name = "管理后台 - 预约")
@RestController
@RequestMapping("/lfd/appointment")
@Validated
public class AppointmentController {

    @Resource
    private AppointmentService appointmentService;
    @Resource
    private ShopService shopService;
    @Autowired
    private PayOrderService payOrderService;
    @Autowired
    private WxPayService wxService;

    @PostMapping("/create")
    @Operation(summary = "创建预约")
    @PreAuthorize("@ss.hasPermission('lfd:appointment:create')")
    public CommonResult<Long> createAppointment(@Valid @RequestBody AppointmentCreateReqVO createReqVO) {
        return success(appointmentService.createAppointment(createReqVO));
    }

    @PutMapping("/update")
    @Operation(summary = "更新预约")
    @PreAuthorize("@ss.hasPermission('lfd:appointment:update')")
    public CommonResult<Boolean> updateAppointment(@Valid @RequestBody AppointmentUpdateReqVO updateReqVO) {
        appointmentService.updateAppointment(updateReqVO);
        return success(true);
    }

    @DeleteMapping("/delete")
    @Operation(summary = "删除预约")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('lfd:appointment:delete')")
    public CommonResult<Boolean> deleteAppointment(@RequestParam("id") Long id) {
        appointmentService.deleteAppointment(id);
        return success(true);
    }

    @GetMapping("/get")
    @Operation(summary = "获得预约")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('lfd:appointment:query')")
    public CommonResult<AppointmentRespVO> getAppointment(@RequestParam("id") Long id) {
        AppointmentDO appointment = appointmentService.getAppointment(id);
        return success(AppointmentConvert.INSTANCE.convert(appointment));
    }

    @Resource
    private BarberService barberService;

    @GetMapping("/list")
    @Operation(summary = "获得预约列表")
    @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
    @PreAuthorize("@ss.hasPermission('lfd:appointment:query')")
    public CommonResult<List<AppointmentRespVO>> getAppointmentList(@RequestParam("ids") Collection<Long> ids) {
        List<AppointmentDO> list = appointmentService.getAppointmentList(ids);
        List<AppointmentRespVO> appointmentRespVOS = AppointmentConvert.INSTANCE.convertList(list);
        for (AppointmentRespVO appointmentRespVO : appointmentRespVOS) {
            BarberDO barber = barberService.getBarber(appointmentRespVO.getBarberId());
            appointmentRespVO.setBarberName(barber.getNickName());
        }
        return success(appointmentRespVOS);
    }

    @GetMapping("/page")
    @Operation(summary = "获得预约分页")
    @PreAuthorize("@ss.hasPermission('lfd:appointment:query')")
    public CommonResult<PageResult<AppointmentListDO>> getAppointmentPage(@Valid AppointmentForm appointmentForm) {
        PageResult<AppointmentListDO> pageResult = appointmentService.getAppointmentPage(appointmentForm);
        return success(pageResult);
    }

    @GetMapping("/status")
    @Operation(summary = "状态")
    public CommonResult<List<Pair>> getAppointmentStatus() {
        List<Pair> collect = Arrays.stream(AppointmentEnum.values()).map(v -> new Pair(v.name(), v.getDesc())).collect(Collectors.toList());
        return success(collect);
    }

    @GetMapping("/appointment-statistic")
    @Operation(summary = "获得预约分页")
    public CommonResult<List<AppointmentStatictis>> getAppointmentStatic(LocalDateTime start, LocalDateTime end) {
        List<AppointmentStatictis> pageResult = appointmentService.getAppointmentStatic(start, end);
        AppointmentStatictis reduce = pageResult.stream().reduce(new AppointmentStatictis("", "总计", BigDecimal.ZERO), AppointmentStatictis::add);
        pageResult.add(reduce);
        return success(pageResult);
    }

    @GetMapping("/export-excel")
    @Operation(summary = "导出预约 Excel")
    @PreAuthorize("@ss.hasPermission('lfd:appointment:export')")
    @OperateLog(type = EXPORT)
    public void exportAppointmentExcel(@Valid AppointmentExportReqVO exportReqVO,
                                       HttpServletResponse response) throws IOException {
        List<AppointmentDO> list = appointmentService.getAppointmentList(exportReqVO);
        // 导出 Excel
        List<AppointmentExcelVO> datas = AppointmentConvert.INSTANCE.convertList02(list);
        ExcelUtils.write(response, "预约.xls", "数据", AppointmentExcelVO.class, datas);
    }


    @GetMapping("/mini-get")
    @Operation(summary = "获得订单状态")
    @Parameter(name = "openId", description = "openId", required = true, example = "1024")
    @Parameter(name = "status", description = "状态", required = true, example = "WAIT")
    @PermitAll
    public CommonResult<List<AppointmentRespVO>> getAppointment(@RequestParam("openId") String openId, AppointmentEnum status) {
        List<AppointmentDO> list = appointmentService.getAppointmentList(openId, status);
        List<AppointmentRespVO> appointmentRespVOS = new ArrayList<>();
        for (AppointmentDO appointmentDO : list) {
            AppointmentRespVO appointmentRespVO = AppointmentConvert.INSTANCE.convert(appointmentDO);
            LfdShopDO shop = shopService.getShop(appointmentRespVO.getShopId());
            BarberDO barber = barberService.getBarber(appointmentRespVO.getBarberId());
            appointmentRespVO.setBarberName(barber.getNickName());
            appointmentRespVO.setAppointmentItems(appointmentDO.getItems());
            appointmentRespVO.setShopName(shop.getName());
            appointmentRespVO.setStatusValue(appointmentRespVO.getStatus().getDesc());
            if (appointmentRespVO.getStatus() == AppointmentEnum.WAIT) {
                Long waitCount = appointmentService.getWaitCount(appointmentRespVO.getId());
                Long waitTime = appointmentService.waitTime(appointmentRespVO.getId());
                appointmentRespVO.setCostTime(waitTime);
                appointmentRespVO.setWaitCount(waitCount);
            }
            appointmentRespVOS.add(appointmentRespVO);
        }
        return success(appointmentRespVOS);
    }

    @PostMapping("/mini-appointment")
    @Operation(summary = "小程序下单")
    @PermitAll
    public CommonResult<Boolean> appointment(@RequestBody @Valid MiniAppointment miniAppointment) {
        appointmentService.appointment(miniAppointment.getOpenId(), miniAppointment.getShopId(), miniAppointment.getBarberId(), miniAppointment.getItems());
        return success(true);
    }

    @GetMapping("/mini-appointment-cancel")
    @Operation(summary = "取消订单")
    @PermitAll
    public CommonResult<Boolean> appointmentCancel(@RequestParam("appointmentId") Long appointmentId) {
        appointmentService.cancel(appointmentId);
        return success(true);
    }

    @PostMapping("/mini-appointment-barbering")
    @Operation(summary = "开始理发")
    @PermitAll
    @Valid
    public CommonResult<Boolean> appointmentbarbering(@RequestParam("appointmentId") @NotNull Long appointmentId) {
        appointmentService.barbering(appointmentId);
        return success(true);
    }

    @PostMapping("/mini-appointment-unpaid")
    @Operation(summary = "待付款,理发师理完发点击")
    @PermitAll
    @Valid
    public CommonResult<Boolean> appointmentUnpaid(@RequestParam("appointmentId") @NotNull Long appointmentId) {
        appointmentService.unpaid(appointmentId);
        return success(true);
    }

    @PostMapping("/mini-appointment-finish")
    @Operation(summary = "完成")
    @PermitAll
    @Valid
    public CommonResult<Boolean> appointmentFinish(@RequestParam("appointmentId") @NotNull Long appointmentId, PayTypeEnum payType) {
        appointmentService.orderFinish(appointmentId, payType);
        return success(true);
    }


    @GetMapping("/unifiedOrder")
    @PermitAll
    public CommonResult unifiedOrder(Long id) throws Exception {
        Object commonResult = payOrderService.unifiedOrder(id);
        return CommonResult.success(commonResult);
    }

    @PostMapping("wx-pay-notify")
    @PermitAll
    public Map<String, Object> payNotify(HttpServletRequest request, HttpServletResponse response) throws IOException, WxPayException {
        byte[] bytes = StreamUtils.copyToByteArray(request.getInputStream());
        WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = wxService.parseOrderNotifyV3Result(new String(bytes), null);
        WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult();
        payOrderService.success(result.getTradeState(), result.getOutTradeNo());
        final Map<String, Object> map = new LinkedHashMap<>();
        map.put("code", "SUCCESS");
        map.put("message", "OK");
        return map;
    }
}
